﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using Go;

namespace fhs
{
    abstract class BaseAction
    {
        static public async Task Go(PointName.Point pointName, int tm = -1)
        {
            Dictionary<PointName.Axis, PointMgr.Position> ptPos;
            if (PointMgr.commPoints.TryGetValue(pointName, out ptPos) || PointMgr.productPoints.TryGetValue(pointName, out ptPos))
            {
                List<PointMgr.Position> axiss = new List<PointMgr.Position>(ptPos.Count);
                {
                    foreach (var item in ptPos)
                    {
                        axiss.Add(item.Value);
                    }
                    axiss.Sort();
                }
                {
                    generator.children children = new generator.children();
                    for (int i = 0; i < axiss.Count;)
                    {
                        int pri = axiss[i].Pri;
                        for (;i < axiss.Count; i++)
                        {
                            if (pri == axiss[i].Pri)
                            {
                                children.go(functional.bind(Abs, axiss[i].Name, axiss[i], -1));
                            }
                            else
                            {
                                break;
                            }
                        }
                        if (children.count != (await children.timed_wait_all(tm)).Count)
                        {
                            await children.stop();
                            foreach (var item in ptPos)
                            {
                                await Stop(item.Value.Name);
                            }
                            await AutoAction.Warning($"{pointName} 点位运动超时");
                            await Go(pointName, tm);
                        }
                    }
                }
            }
            else
            {
                LogMgr.Error($"{pointName} 点位未定义");
                await generator.hold();
            }
        }

        static public async Task Rel(PointName.Axis axisName, PointMgr.Position pos, int tm = -1)
        {
            MotionBase mot;
            if (MotionMgr.motions.TryGetValue(axisName, out mot))
            {
                mot.Vec = pos.Vec;
                mot.Acc = pos.Acc;
                mot.Dec = pos.Dec;
                mot.Rel(pos.Pos);
                await WaitMot(mot, tm);
            }
            else
            {
                LogMgr.Error($"{axisName} 轴未定义");
                await generator.hold();
            }
        }

        static public async Task Rel(PointName.Axis axisName, int pos, int tm = -1)
        {
            MotionBase mot;
            if (MotionMgr.motions.TryGetValue(axisName, out mot))
            {
                mot.Rel(pos);
                await WaitMot(mot, tm);
            }
            else
            {
                LogMgr.Error($"{axisName} 轴未定义");
                await generator.hold();
            }
        }

        static public async Task Abs(PointName.Axis axisName, PointMgr.Position pos, int tm = -1)
        {
            MotionBase mot;
            if (MotionMgr.motions.TryGetValue(axisName, out mot))
            {
                mot.Vec = pos.Vec;
                mot.Acc = pos.Acc;
                mot.Dec = pos.Dec;
                mot.Abs(pos.Pos);
                await WaitMot(mot, tm);
            }
            else
            {
                LogMgr.Error($"{axisName} 轴未定义");
                await generator.hold();
            }
        }

        static public async Task Abs(PointName.Axis axisName, int pos, int tm = -1)
        {
            MotionBase mot;
            if (MotionMgr.motions.TryGetValue(axisName, out mot))
            {
                mot.Abs(pos);
                await WaitMot(mot, tm);
            }
            else
            {
                LogMgr.Error($"{axisName} 轴未定义");
                await generator.hold();
            }
        }

        static public async Task Abs(IList<PointName.Axis> axisNames, IList<PointMgr.Position> poss, int tm = -1)
        {
            Trace.Assert(axisNames.Count == poss.Count);
            generator.children children = new generator.children();
            for (int i = 0; i < axisNames.Count; i++)
            {
                children.go(functional.bind(Abs, axisNames[i], poss[i], tm));
            }
            await children.wait_all();
        }

        static public async Task Abs(IList<PointName.Axis> axisNames, IList<int> poss, int tm = -1)
        {
            Trace.Assert(axisNames.Count == poss.Count);
            generator.children children = new generator.children();
            for (int i = 0; i < axisNames.Count; i++)
            {
                children.go(functional.bind(Abs, axisNames[i], poss[i], tm));
            }
            await children.wait_all();
        }

        static public async Task Rel(IList<PointName.Axis> axisNames, IList<PointMgr.Position> poss, int tm = -1)
        {
            Trace.Assert(axisNames.Count == poss.Count);
            generator.children children = new generator.children();
            for (int i = 0; i < axisNames.Count; i++)
            {
                children.go(functional.bind(Rel, axisNames[i], poss[i], tm));
            }
            await children.wait_all();
        }

        static public async Task Rel(IList<PointName.Axis> axisNames, IList<int> poss, int tm = -1)
        {
            Trace.Assert(axisNames.Count == poss.Count);
            generator.children children = new generator.children();
            for (int i = 0; i < axisNames.Count; i++)
            {
                children.go(functional.bind(Rel, axisNames[i], poss[i], tm));
            }
            await children.wait_all();
        }

        static public async Task RobotGo(PointName.Axis xAxis, PointName.Axis yAxis, PointName.Axis zAxis, int xPos, int yPos, int zPos, int tm = -1)
        {
            await Abs(zAxis, 0, tm);
            await Abs(new PointName.Axis[] { xAxis, yAxis }, new int[] { xPos, yPos }, tm);
            await Abs(zAxis, zPos, tm);
        }

        static public Task Stop(PointName.Point pointName)
        {
            Dictionary<PointName.Axis, PointMgr.Position> ptPos;
            if (PointMgr.commPoints.TryGetValue(pointName, out ptPos) || PointMgr.productPoints.TryGetValue(pointName, out ptPos))
            {
                generator.children children = new generator.children();
                foreach (var axis in ptPos)
                {
                    children.go(() => Stop(axis.Key));
                }
                return children.wait_all();
            }
            else
            {
                LogMgr.Error($"{pointName} 点位未定义");
                return generator.hold();
            }
        }

        static public Task SafeStop(PointName.Point pointName)
        {
            Dictionary<PointName.Axis, PointMgr.Position> ptPos;
            if (PointMgr.commPoints.TryGetValue(pointName, out ptPos) || PointMgr.productPoints.TryGetValue(pointName, out ptPos))
            {
                generator.children children = new generator.children();
                foreach (var axis in ptPos)
                {
                    children.go(() => SafeStop(axis.Key));
                }
                return children.wait_all();
            }
            else
            {
                LogMgr.Error($"{pointName} 点位未定义");
                return generator.hold();
            }
        }

        static public Task Stop(PointName.Axis axisName)
        {
            MotionBase mot;
            if (MotionMgr.motions.TryGetValue(axisName, out mot))
            {
                mot.Stop(false);
                return generator.non_async();
            }
            else
            {
                LogMgr.Error($"{axisName} 轴未定义");
                return generator.hold();
            }
        }

        static public async Task SafeStop(PointName.Axis axisName)
        {
            await Stop(axisName);
            await WaitMot(axisName, 1000);
        }

        static public async Task Home(PointName.Axis axisName, int tm = -1)
        {
            MotionBase mot;
            if (MotionMgr.motions.TryGetValue(axisName, out mot))
            {
                mot.Home();
                await generator.sleep(300);
                await WaitMot(mot, tm);
                await generator.sleep(300);
                mot.Zero();
            }
            else
            {
                LogMgr.Error($"{axisName} 轴未定义");
                await generator.hold();
            }
        }

        static public async Task WaitMot(PointName.Axis axisName, int tm = -1)
        {
            MotionBase mot;
            if (MotionMgr.motions.TryGetValue(axisName, out mot))
            {
                await WaitMot(mot, tm);
            }
            else
            {
                LogMgr.Error($"{axisName} 轴未定义");
                await generator.hold();
            }
        }

        static public async Task WaitMot(MotionBase mot, int tm = -1)
        {
            if (tm < 0)
            {
                while (mot.Run)
                {
                    await generator.sleep(1);
                }
            }
            else
            {
                long bg = system_tick.get_tick_ms();
                while (mot.Run)
                {
                    if (system_tick.get_tick_ms() - bg > tm)
                    {
                        mot.Stop(false);
                        await AutoAction.Warning($"{mot.Name} 轴运动超时");
                        await WaitMot(mot, tm);
                        return;
                    }
                    await generator.sleep(1);
                }
            }
        }

        static public async Task WaitIo(IoName.In ioName, InBase.State st, int tm = -1)
        {
            InBase io;
            if (IoMgr.Ins.TryGetValue(ioName, out io))
            {
                if (tm < 0)
                {
                    while (st != io.Stat)
                    {
                        await generator.sleep(1);
                    }
                }
                else
                {
                    long bg = system_tick.get_tick_ms();
                    while (st != io.Stat)
                    {
                        if (system_tick.get_tick_ms() - bg > tm)
                        {
                            await AutoAction.Warning($"{ioName} IO超时");
                            await WaitIo(ioName, st, tm);
                            return;
                        }
                        await generator.sleep(1);
                    }
                }
            }
            else
            {
                LogMgr.Error($"{ioName} IO未定义");
                await generator.hold();
            }
        }

        static public async Task<IoName.In> WaitIoAny(IoName.In[] ioNames, InBase.State st, int tm = -1)
        {
            if (0 == ioNames.Length)
            {
                return (IoName.In)(-1);
            }
            generator.children children = new generator.children();
            IoName.In res = (IoName.In)(-1);
            foreach (IoName.In ioName in ioNames)
            {
                children.go(functional.bind(WaitIo, ioName, st, -1), delegate ()
                {
                    if (-1 == (int)res)
                    {
                        res = ioName;
                    }
                });
            }
            if (null == await children.timed_wait_any(tm))
            {
                await children.stop();
                await AutoAction.Warning("IO超时");
                return await WaitIoAny(ioNames, st, tm);
            }
            await children.stop();
            return res;
        }

        static public async Task<IoName.In> WaitIoAny(IoName.In[] ioNames, InBase.State[] sts, int tm = -1)
        {
            if (0 == ioNames.Length)
            {
                return (IoName.In)(-1);
            }
            generator.children children = new generator.children();
            IoName.In res = (IoName.In)(-1);
            for (int i = 0; i < ioNames.Length; i++)
            {
                IoName.In ioName = ioNames[i];
                children.go(functional.bind(WaitIo, ioName, sts[i], -1), delegate ()
                {
                    if (-1 == (int)res)
                    {
                        res = ioName;
                    }
                });
            }
            if (null == await children.timed_wait_any(tm))
            {
                await children.stop();
                await AutoAction.Warning("IO超时");
                return await WaitIoAny(ioNames, sts, tm);
            }
            await children.stop();
            return res;
        }

        static public async Task WaitIoAll(IoName.In[] ioNames, InBase.State st, int tm = -1)
        {
            if (0 == ioNames.Length)
            {
                return;
            }
            generator.children children = new generator.children();
            foreach (IoName.In ioName in ioNames)
            {
                children.go(functional.bind(WaitIo, ioName, st, -1));
            }
            if (children.count != (await children.timed_wait_all(tm)).Count)
            {
                await children.stop();
                await AutoAction.Warning("IO超时");
                await WaitIoAll(ioNames, st, tm);
            }
        }

        static public async Task WaitIoAll(IoName.In[] ioNames, InBase.State[] sts, int tm = -1)
        {
            if (0 == ioNames.Length)
            {
                return;
            }
            generator.children children = new generator.children();
            for (int i = 0; i < ioNames.Length; i++)
            {
                IoName.In ioName = ioNames[i];
                children.go(functional.bind(WaitIo, ioName, sts[i], -1));
            }
            if (children.count != (await children.timed_wait_all(tm)).Count)
            {
                await children.stop();
                await AutoAction.Warning("IO超时");
                await WaitIoAll(ioNames, sts, tm);
            }
        }
    }
}
